package org.easy4j.framework.core.util;

import org.easy4j.framework.core.config.Config;
import org.easy4j.framework.core.config.DataSourceConfig;
import org.easy4j.framework.core.datasource.DataSourceFactory;

import java.io.*;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author bjliuyong
 * @version 1.0
 * @created date 15-12-24
 */
public class ProjectBuilder {

    private String baseServiceImport ;
    private String baseServiceClass ;

    private String baseControllerImport;
    private String baseControllerClass ;

    /**
     * java -cp /E/git_study_wp/easy4j-framework/easy4j-framework-core/target/easy4j-framework-core-1.0-SNAPSHOT.jar org.easy4j.framework.core.util.ProjectBuilder
     * @param args
     * @throws Exception
     */
    public static void main(String args[]) throws Exception{

        String projectName = "easy4j.blog" ;

        MO mo = new MO();
        mo.setProjectName(projectName);

        String currentDir = System.getProperty("user.dir") ;


        String parent = currentDir +  File.separator  + projectName.replace(".", File.separator) ;

        mkdir(parent) ;

        String src = parent +  File.separator  + "src" ;
        String src_main = src + File.separator + "main"  ;
        String src_test= src + File.separator + "test" ;


        String src_main_webapp =  src_main + File.separator + "webapp" ;


        mkdirs(src_main ,new String[]{"java","resources" }) ;
        mkdirs(src_test ,new String[]{"java" ,"resources"}) ;
        mkdirs(src_main_webapp , new String[]{"WEB-INF" + File.separator + "vm" ,"www"}) ;

        String[] p = new String[]{ "controller" , "dao" , "domain" ,"service","util" };

        String src_base_package = src_main + File.separator  + "java" + File.separator + "com" + File.separator + projectName.replace(".", File.separator) ;

        mkdirs(src_base_package , p) ;

        buildGitIgnore(parent);
        buildPom(parent ,mo);
        buildWebXml(src_main_webapp) ;
        buildEasy4JPropertiesFile(src_main + File.separator + "resources" ,mo);



    }

    private static void buildPom(String path ,MO mo) throws IOException {
        String filename = "pom.xml" ;


        InputStream inputStream = ProjectBuilder.class.getClassLoader().getResourceAsStream("pom.tpl");

        byte[] bytes = new byte[1024*8] ;

        int n = inputStream.read(bytes);
        String content = new String(bytes , 0 ,n ,"UTF-8") ;

        content = content.replaceAll("@#groupId" ,mo.getProjectName()) ;
        content = content.replaceAll("@#artifactId" ,mo.getProjectName()) ;

        inputStream.close();

        write(path, filename, content);
    }

    private static  void buildGitIgnore(String path)  throws IOException {

        String filename = ".gitignore" ;

        String content =  "# Maven #\n" +
                "target/\n\n" +
                "# IDEA #\n" +
                ".idea/\n" +
                "*.iml\n" +
                "*.ipr\n" +
                "*.iws\n\n" +
                "# Eclipse #\n" +
                ".settings/\n" +
                ".classpath\n" +
                ".project";

        write(path ,filename ,content);
    }

    public static void buildWebXml(String path ) throws IOException{
        String content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<web-app version=\"2.5\" xmlns=\"http://java.sun.com/xml/ns/javaee\"\n" +
                "         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
                "         xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd\">\n" +
                "</web-app>" ;

        write(path + File.separator + "WEB-INF" , "web.xml" ,content );
    }

    private static void buildEasy4JPropertiesFile(String path ,MO mo) throws IOException{
        String content = "base.package=com." + mo.getProjectName() +
                "\n" +
                "\n" +
                "#easy4j.view =defualt ,jsp ,velocity  ; if you want to force to use special view , you can set this property\n" +
                "#easy4j.view=" ;
        write(path  , "easy4j.properties",content);
    }


    private static void write(String path ,String filename ,String content ) throws IOException {

        System.out.println(">>>> create file >>>> " + path + filename);
        System.out.println("**********************************************************");
        System.out.println(content);
        System.out.println("**********************************************************");
        BufferedOutputStream bufferedOutputStream = null;

        try{
            File file = new File(path , filename) ;
            if(!file.getParentFile().exists()){
                mkdir(file.getParent());
            }
            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
            bufferedOutputStream.write(content.getBytes("UTF-8"));
            bufferedOutputStream.flush();
        }  finally {
            if(bufferedOutputStream != null )
                bufferedOutputStream.close();
        }
    }

    private static void mkdirs(String parent ,String[] children){

        for (String child : children ) {
            mkdir(parent + File.separator + child);
        }

    }

    private static boolean mkdir(String path){
        System.out.println(">>>> create path >>>> " + path);
        File p = new File(path) ;
        if(p.exists())
            return true ;

        return p.mkdirs() ;
    }

    public String getBaseServiceImport() {
        return baseServiceImport;
    }

    public void setBaseServiceImport(String baseServiceImport) {
        this.baseServiceImport = baseServiceImport;
    }

    public String getBaseServiceClass() {
        return baseServiceClass;
    }

    public void setBaseServiceClass(String baseServiceClass) {
        this.baseServiceClass = baseServiceClass;
    }

    public String getBaseControllerClass() {
        return baseControllerClass;
    }

    public void setBaseControllerClass(String baseControllerClass) {
        this.baseControllerClass = baseControllerClass;
    }

    public String getBaseControllerImport() {
        return baseControllerImport;
    }

    public void setBaseControllerImport(String baseControllerImport) {
        this.baseControllerImport = baseControllerImport;
    }

    static class MO {

        private String projectName ;

        String getProjectName() {
            return projectName;
        }

        void setProjectName(String projectName) {
            this.projectName = projectName;
        }
    }

    public void genCode(String basePackage ,String tableName) throws Exception {

        genCode(basePackage ,tableName ,
                Config.getValue(DataSourceConfig.JDBC_URL),
                Config.getValue(DataSourceConfig.JDBC_USERNAME),
                Config.getValue(DataSourceConfig.JDBC_PASSWORD)
        );

    }

    public void genCode(String basePackage ,String tableName ,String jdbcUrl ,String username,String password) throws Exception{

        ResultSet rs = null;
        Connection conn = null;
        String javaName = buildClassName(tableName) ;


        String src_main_base_package = "src" + File.separator + "main" + File.separator + "java" + File.separator + basePackage.replace("." ,File.separator);
        String path     = System.getProperty("user.dir") + File.separator + src_main_base_package ;

        String domainPkg     = basePackage + ".domain" ;
        String daoPkg        = basePackage + ".dao"  ;
        String servicePkg    = basePackage + ".service"  ;
        String controllerPkg = basePackage + ".controller"  ;

        StringBuilder daoContent = new StringBuilder("package " + daoPkg + ";\n\n");
        daoContent.append("import org.easy4j.framework.core.orm.BaseDao;\n")
                .append("import ").append(domainPkg).append(".").append(javaName).append(";\n\n")
                .append("@org.springframework.stereotype.Repository\n")
                .append("public class #javaName#Dao extends BaseDao<#javaName#> {\n").append("}") ;

        StringBuilder serviceContent = new StringBuilder("package " + servicePkg + ";\n\n") ;
        if(baseServiceImport != null )
            serviceContent.append(baseServiceImport).append(";\n");
        serviceContent.append("import org.springframework.stereotype.Service;\n");
        serviceContent.append("import ").append(domainPkg).append(".").append(javaName).append(";\n\n");
        serviceContent.append("@Service\n");
        serviceContent.append("public class #javaName#Service ");
        if(baseServiceClass != null )
            serviceContent.append(" extends ").append(baseServiceClass).append("<#javaName#>");
        serviceContent.append("{\n}");

        StringBuilder controllerContent = new StringBuilder("package " + controllerPkg + ";\n\n")  ;
        if(baseControllerImport != null )
            controllerContent.append(baseControllerImport).append(";\n");
        controllerContent.append("import org.springframework.stereotype.Controller ;\n") ;
        controllerContent.append("import org.springframework.web.bind.annotation.RequestMapping ;\n") ;
        controllerContent.append("import ").append(domainPkg).append(".").append(javaName).append(";\n\n");
        controllerContent.append("@Controller\n");
        controllerContent.append("@RequestMapping(\"/").append(javaName.toLowerCase()).append("\")\n");
        controllerContent.append("public class #javaName#Controller ");
        if(baseControllerClass != null )
            controllerContent.append(" extends ").append(baseControllerClass).append("<#javaName#>");
        controllerContent.append("{\n}");

        StringBuilder domainContent = new StringBuilder( "package " + domainPkg + ";\n\n");
        domainContent.append("public class ").append(javaName).append(" { \n\n") ;

        StringBuilder columnGetSetContent = new StringBuilder() ;

        Map map = new HashMap();
        try{
            conn = getConnection("com.mysql.jdbc.Driver" ,jdbcUrl ,username ,password) ;
            DatabaseMetaData dbMetaData = conn.getMetaData();
            rs = dbMetaData.getColumns(null, null, tableName, null);

            while (rs.next()) {
                String columnName = rs.getString("COLUMN_NAME").toLowerCase();
                int dataType = rs.getInt("DATA_TYPE");
                String typeName = rs.getString("TYPE_NAME");
                String javaType = getJavaType(typeName);
                if(javaType == null )
                    javaType = getType(dataType);
                String javaColumn = toFirstCharLowCase(columnName);
                domainContent.append("\t").append("private ").append(javaType).append(" ").append(javaColumn).append(" ;\n\n");

                columnGetSetContent.append("\tpublic ").append(javaType).append(" get").append(buildClassName(columnName))
                        .append("(){ return ").append(javaColumn).append(" ;").append(" }\n") ;

                columnGetSetContent.append("\tpublic void set").append(buildClassName(columnName))
                        .append("(").append(javaType).append(" ").append(javaColumn).append(") { this.")
                        .append(javaColumn).append(" = ").append(javaColumn).append(" ; }\n\n") ;

                map.put(columnName, dataType);
            }
            domainContent.append(columnGetSetContent.toString()) ;
            domainContent.append("}");

            write(path , File.separator + "domain"  + File.separator + javaName + ".java" ,domainContent.toString());
            write(path , File.separator + "dao"  + File.separator  + javaName + "Dao.java" ,daoContent.toString().replace("#javaName#",javaName));
            write(path , File.separator + "service"  + File.separator  + javaName + "Service.java" ,serviceContent.toString().replace("#javaName#",javaName));
            write(path , File.separator + "controller"  + File.separator  + javaName + "Controller.java" ,controllerContent.toString().replace("#javaName#",javaName));

        }  finally {
            if(rs != null ) { rs.close();  rs = null ; }
            if(conn != null ) { conn.close();  conn = null ; }
        }

    }


    private static Connection getConnection(String jdbcDriver ,String jdbcUrl ,String username,String password) throws Exception{
        Class.forName(jdbcDriver);
        return DriverManager.getConnection(jdbcUrl,username,password);
    }

    protected static String buildClassName(String table) {

        StringBuilder sb = new StringBuilder();
        boolean b = true;
        char c;
        for (int i = 0; i < table.length(); i++) {
            c = table.charAt(i);

            if (c == '_') {
                b = true;
                continue;
            }

            if (b)
                sb.append(Character.toUpperCase(c));
            else
                sb.append(c);
            b = false;
        }
        return sb.toString();
    }

    protected static String toUpperCase(String field) {

        StringBuilder sb = new StringBuilder();
        boolean b = true;
        char c;
        for (int i = 0; i < field.length(); i++) {
            c = field.charAt(i);

            if (c == '_') {
                b = true;
                continue;
            }

            if (b)
                sb.append(Character.toUpperCase(c));
            else
                sb.append(c);
            b = false;
        }
        return sb.toString();
    }

    protected  static String toFirstCharLowCase(String field) {

        field = field.toLowerCase();
        StringBuilder sb = new StringBuilder();
        boolean b = false;
        char c;
        for (int i = 0; i < field.length(); i++) {
            c = field.charAt(i);

            if (i == 0) {
                sb.append(Character.toLowerCase(c));
                continue;
            }

            if (c == '_') {
                b = true;
                continue;
            }

            if (b)
                sb.append(Character.toUpperCase(c));
            else
                sb.append(c);
            b = false;
        }
        return sb.toString();
    }

    private static String getJavaType(String typeName) {

        String javaType = null;


        if(typeName.equals("VARCHAR")) {
            javaType = "java.lang.String" ;
        }

        if(typeName.equals("ENUM")) {
            javaType = "java.lang.String" ;
        }

        if(typeName.equals("TIMESTAMP")) {
            javaType = "java.util.Date" ;
        }



        return javaType ;

    }

    protected static String getType(int type) {

        String javaType = "java.lang.String";

        switch (type) {
            case Types.TIMESTAMP:
                javaType = "java.util.Date" ;
                break;
            case Types.VARCHAR:
                break;

            case Types.INTEGER:
                javaType = "int" ;
                break;
            case Types.SMALLINT:
                javaType = "int" ;
                break;

            case Types.TINYINT:
                javaType = "int" ;
                break;

            case Types.BIGINT:
                javaType = "long" ;
                break;
            case Types.DATE:
                javaType = "java.util.Date";
                break;

            case Types.CHAR :
                javaType = "char" ;
                break;

        }
        return javaType;

    }



}
